package megamu.mesh;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;

import com.jellyfishumbrella.GL.AbstractLOD;
import com.jellyfishumbrella.GL.MeshGL;
import com.jellyfishumbrella.GL.Normal;
import com.jellyfishumbrella.GL.PolygonGroup;
import com.jellyfishumbrella.GL.Vertex;

public class Region extends PolygonGroup {
	public Site						site;
	private LinkedHashSet<Corner>	corners		= new LinkedHashSet<Corner>();
	private LinkedHashSet<Edge>		edges		= new LinkedHashSet<Edge>();
	public LinkedHashSet<Region>	neighbors	= new LinkedHashSet<Region>();
	int								count;

	private CreateGLpolygonThread createGLpolygonThread = new CreateGLpolygonThread(true);

	public Region() {
		this(0);
	}

	public Region(int points) {
		super();
		corners = new LinkedHashSet<Corner>();
		edges = new LinkedHashSet<Edge>();
		neighbors = new LinkedHashSet<Region>();
		count = 0;

		maxLOD = 2;
	}

	public void addCorner(float x, float z) {
		Corner newCorner = new Corner(x, 0, z);
		newCorner.addRegion(this);
		corners.add(newCorner);
	}

	public void addCorner(Corner corner) {
		corner.addRegion(this);
		corners.add(corner);
	}

	public void addEdge(Edge edge) {
		edges.add(edge);
	}

	public int vertCount() {
		return corners.size();
	}

	public ArrayList<Corner> getCorners() {
		ArrayList<Corner> corners2 = new ArrayList<Corner>(corners);

		// for(Corner corner : corners){
		// corners2.add(corner);
		// }
		return corners2;
	}

	public ArrayList<Vertex> getVerts() {
		ArrayList<Vertex> verts = new ArrayList<Vertex>(corners);

		// for(Corner corner : corners){
		// verts.add(corner);
		// }
		return verts;
	}

	public String toString() {
		String str = "MPolygon: ";
		for (Corner corner : corners) {
			str += corner.toString();
		}
		return str;
	}

	public void pointNormalsUp() {
		ArrayList<Corner> corners2 = new ArrayList<Corner>(corners);
		if (corners2.size() >= 2) {
			Normal normal = computeTriangleNormal(corners2.get(1), corners2.get(2));
			double dotProduct = normal.x * 0 + normal.y * 1 + normal.z * 0;
			if (dotProduct > 0) {
				Collections.reverse(corners2);
				corners = new LinkedHashSet<Corner>(corners2);
			}
		}
	}

	public Normal computeTriangleNormal(Corner corner1, Corner corner2) {
		Normal a = new Normal();// normals
		Normal b = new Normal();// normals

		Normal normal = new Normal();
		normal.x = 0.0f;
		normal.y = 0.0f;
		normal.z = 0.0f;

		a.x = corner1.x - site.x;
		a.y = corner1.y - site.y;
		a.z = corner1.z - site.z;

		b.x = corner2.x - site.x;
		b.y = corner2.y - site.y;
		b.z = corner2.z - site.z;

		normal.x = (float) (b.y * a.z - b.z * a.y);
		normal.y = (float) (b.z * a.x - b.x * a.z);
		normal.z = (float) (b.x * a.y - b.y * a.x);

		normal.normalize();
		return normal;
	}

	public void computeNormal() {
		ArrayList<Corner> corners2 = new ArrayList<Corner>(corners);

		Normal avgNormal = new Normal();
		avgNormal.x = 0.0f;
		avgNormal.y = 0.0f;
		avgNormal.z = 0.0f;

		for (int i = 1; i < corners2.size() - 1; i++) {
			Normal normal = computeTriangleNormal(corners2.get(i), corners2.get(i + 1));

			avgNormal.x += normal.x;
			avgNormal.y += normal.y;
			avgNormal.z += normal.z;
		}

		avgNormal.x /= corners2.size();
		avgNormal.y /= corners2.size();
		avgNormal.z /= corners2.size();

		avgNormal.normalize();

		corners2.get(0).normal = site.normal = avgNormal;
	}

	public synchronized LinkedHashSet<AbstractLOD> preSetLOD(int newLOD) {
		LinkedHashSet<AbstractLOD> needsUpdate = new LinkedHashSet<AbstractLOD>();
		if (newLOD >= 0) {
			if (newLOD != LOD || newLOD > nextLOD) {
				needsUpdate.add(this);
				nextLOD = newLOD;
			}
			Iterator itr = this.neighbors.iterator();
			while (itr.hasNext()) {
				Region currNeighbor = (Region) itr.next();
				needsUpdate.addAll(currNeighbor.preSetLOD(newLOD - 1));
			}
		}
		return needsUpdate;
	}

	public synchronized void updateLOD() {
		setLOD(nextLOD);
	}

	public int setLOD(int newLOD) {
		if (newLOD >= 0 && newLOD != LOD) {
			LOD = Math.min(maxLOD, newLOD);
			// Iterator itr = this.neighbors.iterator();
			// while (itr.hasNext()) {
			// Region currNeighbor = (Region) itr.next();
			// currNeighbor.setLOD(newLOD - 1);
			// }
			createGLpolygonThread.run();
			// createGLpolygonThread.start();
		}
		return LOD;
	}

	public int incLOD() {
		if (LOD < maxLOD) {
			LOD++;
			// createGLpolygon(true);
			CreateGLpolygonThread createGLpolygonThread = new CreateGLpolygonThread(true);
			// createGLpolygonThread.start();
		}
		return LOD;
	}

	public int decLOD() {
		if (LOD > 0) {
			LOD--;
			// createGLpolygon(true);
			CreateGLpolygonThread createGLpolygonThread = new CreateGLpolygonThread(true);
			// createGLpolygonThread.start();
		}
		return LOD;
	}

	public class CreateGLpolygonThread extends Thread {
		boolean	immediateUpdate	= true;

		public CreateGLpolygonThread(boolean immediateUpdate) {
			this.immediateUpdate = immediateUpdate;
		}

		public void run() {
			createGLpolygon(immediateUpdate);
		}

		public synchronized void createGLpolygon(boolean immediateUpdate) {
			parentMesh.removePolygonGroup(Region.this);
			// polygons.clear();
			switch (LOD) {
				case 0:
					createGLpolygonLOD0(parentMesh);
					break;
				case 1:
					createGLpolygonLOD1(parentMesh);
					break;
				case 2:
					createGLpolygonLOD2(parentMesh);
					break;
			}
			if (immediateUpdate) {
				parentMesh.refreshVerts = true;
				parentMesh.refreshIndices = true;
			}
		}

		public synchronized void createGLpolygonLOD0(MeshGL parentMesh) {
			ArrayList<Vertex> points = getVerts();
			parentMesh.addPolygonFan(Region.this, points, true);
		}

		public synchronized void createGLpolygonLOD1(MeshGL parentMesh) {
			ArrayList<Vertex> points = getVerts();
			parentMesh.addBufferedPolygonFan(Region.this, points, 0.333, true);
		}

		public synchronized void createGLpolygonLOD2(MeshGL parentMesh) {
			ArrayList<Vertex> points = getVerts();
			parentMesh.addBufferedPolygonFan(Region.this, points, 0.666, true);
		}
	}
}